﻿using System.IO;
using System.Text;
using System.Web;
using System.Xml;

namespace Wechat
{
    using System;
    using Wechat.Events;
    using Wechat.Results;

    public interface IWechatController
    {
        IWechatResult Handle(WechatTextMessage evnt);

        IWechatResult Handle(WechatImageMessage evnt);

        IWechatResult Handle(WechatVoiceMessage evnt);

        IWechatResult Handle(WechatVideoMessage evnt);

        IWechatResult Handle(WechatMapMessage evnt);

        IWechatResult Handle(WechatLinkMessage evnt);

        IWechatResult Handle(WechatLocationEvent evnt);

        IWechatResult Handle(WechatClickEvent evnt);
    }

    public abstract class WechatController : IWechatController
    {
        private string _token;

        private WechatToken _wechatToken;

        private WechatEvent _wechatEvent;

        /// <summary>
        /// 创建微信控制器
        /// </summary>
        /// <param name="token">用户凭据</param>
        public WechatController(string token)
        {
            _token = token;
        }


        private XmlElement GetElement(Stream stream)
        {
            if (stream == null || stream.Length == 0) { return null; }

            var bytes = new byte[stream.Length];

            stream.Read(bytes, 0, (int)stream.Length);

            var document = new XmlDocument();

            document.LoadXml(Encoding.UTF8.GetString(bytes));

            return document.DocumentElement;
        }

        private WechatToken GetToken(HttpRequest request)
        {
            var token = new WechatToken();
            token.Nonce = request["nonce"];
            token.Echostr = request["echostr"];
            token.Signature = request["signature"];
            token.Timestamp = request["timestamp"];
            return token;
        }

        private TEvent GetEvent<TEvent>(XmlElement element)
            where TEvent : WechatEvent, new()
        {
            TEvent evnt = new TEvent();

            evnt.ToUsername = element.SelectSingleNode("ToUserName").InnerText;

            evnt.FromUserName = element.SelectSingleNode("FromUserName").InnerText;

            evnt.CreateTime = long.Parse(element.SelectSingleNode("CreateTime").InnerText);

            return evnt;
        }

        private TMessage GetMessage<TMessage>(XmlElement element)
            where TMessage : WechatMessage, new()
        {
            TMessage message = GetEvent<TMessage>(element);

            message.MsgID = long.Parse(element.SelectSingleNode("MsgId").InnerText);
            
            return message;
        }

        private WechatMapMessage GetMapMessage(XmlElement element)
        {
            var evnt = GetMessage<WechatMapMessage>(element);

            evnt.Scale = int.Parse(element.SelectSingleNode("Scale").InnerText);

            evnt.Lable = element.SelectSingleNode("Label").InnerText;

            evnt.Location_X = double.Parse(element.SelectSingleNode("Location_X").InnerText);

            evnt.Location_Y = double.Parse(element.SelectSingleNode("Location_Y").InnerText);

            _wechatEvent = evnt;

            return evnt;
        }

        private WechatLinkMessage GetLinkMessage(XmlElement element)
        {
            var evnt = GetMessage<WechatLinkMessage>(element);

            evnt.Url = element.SelectSingleNode("Url").InnerText;

            evnt.Title = element.SelectSingleNode("Title").InnerText;

            evnt.Description = element.SelectSingleNode("Description").InnerText;

            _wechatEvent = evnt;

            return evnt;
        }

        private WechatTextMessage GetTextMessage(XmlElement element)
        {
            var evnt = GetMessage<WechatTextMessage>(element);
            
            evnt.Content = element.SelectSingleNode("Content").InnerText;
            
            _wechatEvent = evnt;
            
            return evnt;
        }

        private WechatImageMessage GetImageMessage(XmlElement element)
        {
            var evnt = GetMessage<WechatImageMessage>(element);
         
            evnt.PicUrl = element.SelectSingleNode("PicUrl").InnerText;
            
            evnt.MediaId = element.SelectSingleNode("MediaId").InnerText;

            _wechatEvent = evnt;

            return evnt;
        }

        private WechatVoiceMessage GetVoiceMessage(XmlElement element)
        {
            var evnt = GetMessage<WechatVoiceMessage>(element);

            evnt.Format = element.SelectSingleNode("Format").InnerText;

            evnt.MediaId = element.SelectSingleNode("MediaId").InnerText;

            _wechatEvent = evnt;

            return evnt;
        }

        private WechatVideoMessage GetVideoMessage(XmlElement element)
        {
            var evnt = GetMessage<WechatVideoMessage>(element);

            evnt.MediaId = element.SelectSingleNode("MediaId").InnerText;

            evnt.ThumbMediaId = element.SelectSingleNode("ThumbMediaId").InnerText;

            _wechatEvent = evnt;

            return evnt;
        }

        private WechatLocationEvent GetLocationEvent(XmlElement element)
        {
            var evnt = GetEvent<WechatLocationEvent>(element);

            evnt.Latitude = double.Parse(element.SelectSingleNode("Latitude").InnerText);

            evnt.Longitude = double.Parse(element.SelectSingleNode("Longitude").InnerText);

            evnt.Precision = double.Parse(element.SelectSingleNode("Precision").InnerText);

            _wechatEvent = evnt;

            return evnt;
        }

        private WechatClickEvent GetClickEvent(XmlElement element)
        {
            var evnt = GetEvent<WechatClickEvent>(element);

            evnt.ButtonID = element.SelectSingleNode("EventKey").InnerText;

            _wechatEvent = evnt;

            return evnt;
        }

        /// <summary>
        /// 执行入口
        /// </summary>
        /// <param name="httpContext"></param>
        public void Execute(HttpContext httpContext)
        {
            var evnt = default(WechatEvent);

            var result = default(IWechatResult);

            try
            {
                httpContext.Response.Clear();

                httpContext.Response.ContentType = "text/xml";

                var wechatToken = GetToken(httpContext.Request);

                if (httpContext.Request.HttpMethod.Equals("GET"))
                {
                    httpContext.Response.ContentType = @"text/plain";

                    httpContext.Response.Write(wechatToken.Echostr);

                    return;
                }

                if (!ValidToken(wechatToken)) { return; }

                var element = GetElement(httpContext.Request.InputStream);

                if (element == null) { return; }

                _wechatToken = wechatToken;

                var msgType = element.SelectSingleNode("MsgType").InnerText;

                switch (msgType)
                {
                    case "text":
                        evnt = GetTextMessage(element);
                        result = Handle(evnt as WechatTextMessage);
                        break;

                    case "image":
                        evnt = GetImageMessage(element);
                        result = Handle(evnt as WechatImageMessage);
                        break;

                    case "voice":
                        evnt = GetVoiceMessage(element);
                        result = Handle(evnt as WechatVoiceMessage);
                        break;

                    case "video":
                        evnt = GetVideoMessage(element);
                        result = Handle(evnt as WechatVideoMessage);
                        break;

                    case "location":
                        evnt = GetMapMessage(element);
                        result = Handle(evnt as WechatMapMessage);
                        break;

                    case "link":
                        evnt = GetLinkMessage(element);
                        result = Handle(evnt as WechatLinkMessage);
                        break;

                    case "event":
                        switch (element.SelectSingleNode("Event").InnerText)
                        {
                            case "CLICK":
                                evnt = GetClickEvent(element);
                                result = Handle(evnt as WechatClickEvent);
                                break;

                            case "LOCATION":
                                evnt = GetLocationEvent(element);
                                result = Handle(evnt as WechatLocationEvent);
                                break;

                            default:
                                result = HandleUnKnowMessage(evnt);
                                break;
                        }
                        break;

                    default:
                        result = HandleUnKnowMessage(evnt);
                        return;
                }
            }
            catch (Exception ex)
            {
                result = OnException(ex);
            }
            
            OnExecuted(result);

            httpContext.Response.Write(result.ToString());
        }

        /// <summary>
        /// 在每次收到请求时调用 用于验证可信来源
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        protected virtual bool ValidToken(WechatToken token)
        {
            return Utilities.ValidSignature(_token, token);
        }

        /// <summary>
        /// 在操作执行后
        /// </summary>
        /// <param name="result"></param>
        protected virtual void OnExecuted(IWechatResult result)
        {

        }

        protected virtual IWechatResult OnException(Exception exception)
        {
            return Empty();
        }

        protected virtual IWechatResult HandleUnKnowMessage(WechatEvent evnt)
        {
            return Empty();
        }

        /// <summary>
        /// 返回空消息
        /// </summary>
        /// <returns></returns>
        protected WechatEmptyResult Empty()
        {
            return new WechatEmptyResult(_wechatEvent.FromUserName, _wechatEvent.ToUsername);
        }

        /// <summary>
        /// 返回文本回复
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        protected WechatTextResult Text(string content)
        {
            return new WechatTextResult(_wechatEvent.FromUserName, _wechatEvent.ToUsername)
            {
                Content = content
            };
        }

        /// <summary>
        /// 返回图片回复
        /// </summary>
        /// <param name="imageId"></param>
        /// <returns></returns>
        protected WechatImageResult Image(string imageId)
        {
            return new WechatImageResult(_wechatEvent.FromUserName, _wechatEvent.ToUsername)
            {
                MediaId = imageId
            };
        }

        /// <summary>
        /// 返回视频回复
        /// </summary>
        /// <param name="videoId"></param>
        /// <param name="title"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        protected WechatVideoResult Video(string videoId, string title, string description)
        {
            return new WechatVideoResult(_wechatEvent.FromUserName, _wechatEvent.ToUsername)
            {
                Title = title,
                MediaId = videoId,
                Description = description
            };
        }

        /// <summary>
        /// 返回语音回复
        /// </summary>
        /// <param name="voiceId"></param>
        /// <returns></returns>
        protected WechatVoiceResult Voice(string voiceId)
        {
            return new WechatVoiceResult(_wechatEvent.FromUserName, _wechatEvent.ToUsername)
            {
                MediaId = voiceId
            };
        }
        
        /// <summary>
        /// 返回图文回复
        /// </summary>
        /// <param name="articles"></param>
        /// <returns></returns>
        protected WechatArticleResult Article(params WechatArticle[] articles)
        {
            return new WechatArticleResult(_wechatEvent.FromUserName, _wechatEvent.ToUsername)
            {
                Items = articles
            };
        }

        /// <summary>
        /// 创建图文
        /// </summary>
        /// <param name="title"></param>
        /// <param name="url"></param>
        /// <param name="picUrl"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        protected WechatArticle CreateArticle(string title = null, string url = null, string picUrl = null, string description = null)
        {
            return new WechatArticle
            {
                Url = url,
                Title = title,
                PicUrl = picUrl,
                Description = description
            };
        }

        /// <summary>
        /// 返回音乐回复
        /// </summary>
        /// <param name="thumbId"></param>
        /// <param name="title"></param>
        /// <param name="musicUrl"></param>
        /// <param name="hqMusicUrl"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        protected WechatMusicResult Music(string thumbId, string title, string musicUrl, string hqMusicUrl, string description)
        {
            return new WechatMusicResult(_wechatEvent.FromUserName, _wechatEvent.ToUsername)
            {
                Title = title,
                MusicURL = musicUrl,
                HQMusicUrl = hqMusicUrl,
                Description = description,
                ThumbMediaId = thumbId
            };
        }

        /// <summary>
        /// 身份凭据
        /// </summary>
        public WechatToken Token
        {
            get { return _wechatToken; }
        }

        /// <summary>
        /// 响应文本消息
        /// </summary>
        /// <param name="evnt"></param>
        /// <returns></returns>
        public virtual IWechatResult Handle(WechatTextMessage evnt)
        {
            return Empty();
        }

        /// <summary>
        /// 响应图片消息
        /// </summary>
        /// <param name="evnt"></param>
        /// <returns></returns>
        public virtual IWechatResult Handle(WechatImageMessage evnt)
        {
            return Empty();
        }

        /// <summary>
        /// 响应语音消息
        /// </summary>
        /// <param name="evnt"></param>
        /// <returns></returns>
        public virtual IWechatResult Handle(WechatVoiceMessage evnt)
        {
            return Empty();
        }

        /// <summary>
        /// 响应视频消息
        /// </summary>
        /// <param name="evnt"></param>
        /// <returns></returns>
        public virtual IWechatResult Handle(WechatVideoMessage evnt)
        {
            return Empty();
        }

        /// <summary>
        /// 响应地图消息
        /// </summary>
        /// <param name="evnt"></param>
        /// <returns></returns>
        public virtual IWechatResult Handle(WechatMapMessage evnt)
        {
            return Empty();
        }

        /// <summary>
        /// 响应链接消息
        /// </summary>
        /// <param name="evnt"></param>
        /// <returns></returns>
        public virtual IWechatResult Handle(WechatLinkMessage evnt)
        {
            return Empty();
        }

        /// <summary>
        /// 响应地理位置
        /// </summary>
        /// <param name="evnt"></param>
        /// <returns></returns>
        public virtual IWechatResult Handle(WechatLocationEvent evnt)
        {
            return Empty();
        }

        /// <summary>
        /// 响应菜单点击
        /// </summary>
        /// <param name="evnt"></param>
        /// <returns></returns>
        public virtual IWechatResult Handle(WechatClickEvent evnt)
        {
            return Empty();
        }
    }
}
